package com;

import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * Gson工具类
 */
public final class GsonUtil {
    private final static DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private final static DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    private final static DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");
    private final static SimpleDateFormat OLD_DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public static Gson gson() {
        return builder().create();
    }

    public static void main(String[] args) {
//        TypeToken typeToken = new TypeToken();
//        GsonUtil.gson().fromJson(str,)
    }
    public static GsonBuilder builder() {
        GsonBuilder builder = new GsonBuilder()
                .setLenient() // 解析Json数据时忽略未知字段
                .disableHtmlEscaping()
                .serializeNulls()
                .excludeFieldsWithModifiers(Modifier.TRANSIENT, Modifier.STATIC)
                .enableComplexMapKeySerialization()
                .disableJdkUnsafe()
                .disableHtmlEscaping();
        // List集合适配器
        builder.registerTypeAdapterFactory(new ListTypeAdapterFactory());
        // 枚举字典适配器
//        builder.registerTypeAdapterFactory(new ApiDictEnumTypeAdapterFactory());
        // 数字处理
//        builder.registerTypeAdapter(int.class,new IntAdapter());
        // 对象不匹配的处理
//        builder.registerTypeAdapterFactory(new ObjAdapterFactory());
        dateAdapter(builder);
        bigDecimalAdapter(builder);
        return builder;
    }

    private static class ObjAdapterFactory implements TypeAdapterFactory {

        @Override
        public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
            Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
            TypeAdapter<?> elementAdapter = gson.getAdapter(TypeToken.get(elementType));
            return new ObjAdapter<>(elementType,elementAdapter);
        }
        private static class ObjAdapter<T,E> extends TypeAdapter<T> {
            private final Type enumType;
            private final TypeAdapter<E> elementAdapter;
            public ObjAdapter(Type enumType,TypeAdapter<E> elementAdapter) {
                this.enumType = enumType;
                this.elementAdapter = elementAdapter;
            }

            @Override
            public void write(JsonWriter out, T value) throws IOException {
                if (isnull(value)) {
                    out.nullValue();
                    return;
                }
                elementAdapter.write(out,(E)value);
            }

            @Override
            public T read(JsonReader in) throws IOException {
                if (in.peek() == JsonToken.NULL) {
                    in.nextNull();
                    return null;
                }
                String value = in.nextString();
                if (isnull(value)) {
                    return null;
                }
                return null;
            }
        }
    }

    // 通用List集合序列化/反序列化适配器
    private static class ListTypeAdapterFactory implements TypeAdapterFactory {
        @SuppressWarnings("all")
        @Override
        public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
            Type type = typeToken.getType();
            if (!List.class.isAssignableFrom(typeToken.getRawType()) || !(type instanceof ParameterizedType)) {
                return null;
            }
            // 拿到List泛型的真正类型-
            Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
            TypeAdapter<?> elementAdapter = gson.getAdapter(TypeToken.get(elementType));
            // 返回一个新的 TypeAdapter 对象，用于处理嵌套的 List 对象
            return new ListTypeAdapter(elementAdapter).nullSafe();
        }

        /**
         * 用于处理嵌套 List 对象的 TypeAdapter。
         */
        private static class ListTypeAdapter<E> extends TypeAdapter<List<E>> {
            private final TypeAdapter<E> typeAdapter;

            private ListTypeAdapter(TypeAdapter<E> typeAdapter) {
                this.typeAdapter = typeAdapter;
            }

            @Override
            public void write(JsonWriter out, List<E> list) throws IOException {
                if (list == null) {
                    out.nullValue();
                    return;
                }
                out.beginArray();
                for (E e : list) {
                    typeAdapter.write(out, e);
                }
                out.endArray();
            }

            @Override
            public List<E> read(JsonReader in) throws IOException {
                JsonToken token = in.peek();
                if (token == JsonToken.NULL) {
                    in.nextNull();
                    return null;
                }
                // 以Java Bean的数据结构为准，如果JSON字符串中是一个对象，但Java Bean中对应的是集合
                // 则需要将 JSON字符串中的对象序列化放进集合返回，保持数据格式统一（必须做兼容性处理）
                List<E> list = new ArrayList<>();
                if (token == JsonToken.BEGIN_OBJECT) {
                    E e = typeAdapter.read(in);
                    list.add(e);
                } else if (token == JsonToken.BEGIN_ARRAY) {
                    in.beginArray();
                    while (in.hasNext()) {
                        E e = typeAdapter.read(in);
                        list.add(e);
                    }
                    in.endArray();
                }
                return list;
            }
        }
    }


    private static boolean isnull(Object value) {
        if (value instanceof CharSequence) {
            return StringUtils.isBlank((CharSequence) value) || "null".equalsIgnoreCase((String) value);
        }
        return null == value;
    }

    /**
     * BigDecimal序列化/反序列化处理
     *
     * @param builder
     * @return
     */
    private static void bigDecimalAdapter(GsonBuilder builder) {
        builder.registerTypeAdapter(BigDecimal.class,
                        (JsonSerializer<BigDecimal>) (src, typeOfSrc, context) -> isnull(src) ? JsonNull.INSTANCE : new JsonPrimitive(src.toPlainString()))
                .registerTypeAdapter(BigDecimal.class, (JsonDeserializer<BigDecimal>) (json, typeOfT, ctx) -> {
                    if (isnull(json)) {
                        return null;
                    }
                    // 去除空格及中英文逗号字符
                    String val = json.getAsJsonPrimitive()
                            .getAsString().replaceAll("\\s", "")
                            .replaceAll(",", "")
                            .replaceAll("，", "");
                    if (isnull(val)) {
                        return null;
                    }
                    return new BigDecimal(val);
                });
    }

    /**
     * 序列化反序列时间日期
     */
    private static void dateAdapter(GsonBuilder builder) {
        // LocalDateTime
        builder.registerTypeAdapter(LocalDateTime.class, (JsonDeserializer<LocalDateTime>) (json, typeOfT, context) -> {
            if (isnull(json)) {
                return null;
            }
            String dateTimeStr = json.getAsJsonPrimitive().getAsString();
            return LocalDateTime.parse(dateTimeStr, DATE_TIME_FORMATTER);
        }).registerTypeAdapter(LocalDateTime.class, (JsonSerializer<LocalDateTime>) (src, typeOfSrc, context) -> {
            if (isnull(src)) {
                return JsonNull.INSTANCE;
            }
            return new JsonPrimitive(DATE_TIME_FORMATTER.format(src));
        });
        // LocalDate
        builder.registerTypeAdapter(LocalDate.class, (JsonDeserializer<LocalDate>) (json, typeOfT, context) -> {
            if (isnull(json)) {
                return null;
            }
            String dateTimeStr = json.getAsJsonPrimitive().getAsString();
            return LocalDate.parse(dateTimeStr, DATE_FORMATTER);
        }).registerTypeAdapter(LocalDate.class, (JsonSerializer<LocalDate>) (src, typeOfSrc, context) -> {
            if (isnull(src)) {
                return JsonNull.INSTANCE;
            }
            return new JsonPrimitive(DATE_FORMATTER.format(src));
        });
        // LocalTime
        builder.registerTypeAdapter(LocalTime.class, (JsonDeserializer<LocalTime>) (json, typeOfT, context) -> {
            if (isnull(json)) {
                return null;
            }
            String dateTimeStr = json.getAsJsonPrimitive().getAsString();
            return LocalTime.parse(dateTimeStr, TIME_FORMATTER);
        }).registerTypeAdapter(LocalTime.class, (JsonSerializer<LocalTime>) (src, typeOfSrc, context) -> {
            if (isnull(src)) {
                return JsonNull.INSTANCE;
            }
            return new JsonPrimitive(TIME_FORMATTER.format(src));
        });
        // Date
        builder.registerTypeAdapter(Date.class, (JsonDeserializer<Date>) (json, typeOfT, context) -> {
            if (isnull(json)) {
                return null;
            }
            String dateTimeStr = json.getAsJsonPrimitive().getAsString();
            try {
                return OLD_DATE_FORMATTER.parse(dateTimeStr);
            } catch (ParseException e) {
                throw new RuntimeException(e);
            }
        }).registerTypeAdapter(Date.class, (JsonSerializer<Date>) (src, typeOfSrc, context) -> {
            if (isnull(src)) {
                return JsonNull.INSTANCE;
            }
            return new JsonPrimitive(OLD_DATE_FORMATTER.format(src));
        });
    }
}
